package com.zhuhao.basic.reflect;

import lombok.SneakyThrows;
import org.junit.Test;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;


// 主类：演示反射API的使用
public class ReflectionDemo {


    /**
     * getMethods()                getDeclaredMethods()
     * 仅获取 public 方法           获取所有声明的方法（包括 private、protected、包访问权限）
     * 包含从父类继承的 public 方法   仅获取当前类声明的方法，不包括继承的方法
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        // 1. 获取Class对象的三种方式
        Class<?> clazz1 = MyClass.class;
        Class<?> clazz2 = new MyClass().getClass();
        Class<?> clazz3 = Class.forName("com.zhuhao.basic.reflect.MyClass"); // 需要完整类名

        System.out.println("=== 1. 获取Class对象 ===");
        System.out.println("clazz1: " + clazz1);
        System.out.println("clazz2: " + clazz2);
        System.out.println("clazz3: " + clazz3);

        // 2. 类的基本信息
        System.out.println("\n=== 2. 类的基本信息 ===");
        System.out.println("类名: " + clazz1.getName());
        System.out.println("简单类名: " + clazz1.getSimpleName());
        System.out.println("是否为接口: " + clazz1.isInterface());
        System.out.println("是否为注解: " + clazz1.isAnnotation());
        System.out.println("是否为枚举: " + clazz1.isEnum());
        System.out.println("是否为基本类型: " + clazz1.isPrimitive());
        Class<? extends Class> aClass = clazz1.getClass();
        System.out.println(aClass);
        Class<?> declaringClass = clazz1.getDeclaringClass();
        System.out.println(declaringClass);


        // 3. 获取构造函数
        System.out.println("\n=== 3. 获取构造函数 ===");
        Constructor<?>[] constructors = clazz1.getConstructors();
        System.out.println("所有公共构造函数: " + Arrays.toString(constructors));

        Constructor<?> noArgConstructor = clazz1.getConstructor();
        Constructor<?> paramConstructor = clazz1.getConstructor(String.class);

        // 创建实例
        Object instance1 = noArgConstructor.newInstance();
        Object instance2 = paramConstructor.newInstance("Hello");
//
//        // 4. 获取字段
//        System.out.println("\n=== 4. 获取字段 ===");
//        Field publicField = clazz1.getField("publicField");
//        Field privateField = clazz1.getDeclaredField("privateField");
//
//        // 访问公共字段
//        System.out.println("publicField before: " + publicField.get(instance1));
//        publicField.set(instance1, "new value");
//        System.out.println("publicField after: " + publicField.get(instance1));
//
//        // 访问私有字段
//        privateField.setAccessible(true); // 打破封装
//        System.out.println("privateField before: " + privateField.get(instance1));
//        privateField.set(instance1, 99);
//        System.out.println("privateField after: " + privateField.get(instance1));
//
        // 5. 获取方法
        System.out.println("\n=== 5. 获取方法 ===");
        Method publicMethod = clazz1.getMethod("publicMethod", String.class);
        Method privateMethod = clazz1.getDeclaredMethod("privateMethod", int.class, int.class);
        Method staticMethod = clazz1.getMethod("staticMethod");

        // 调用公共方法
        publicMethod.invoke(instance1, "test");
//
//        // 调用私有方法
//        privateMethod.setAccessible(true);
//        int result = (int) privateMethod.invoke(instance1, 3, 4);
//        System.out.println("私有方法返回值: " + result);
//
//        // 调用静态方法
//        staticMethod.invoke(null); // 静态方法不需要实例
//
//        // 6. 数组反射
//        System.out.println("\n=== 6. 数组反射 ===");
//        Class<?> arrayClass = Class.forName("[I"); // 整型数组
//        System.out.println("数组类型: " + arrayClass.getName());
//        System.out.println("是否为数组: " + arrayClass.isArray());
//
//        // 创建数组实例
//        int[] intArray = (int[]) Array.newInstance(int.class, 3);
//        Array.set(intArray, 0, 100);
//        Array.set(intArray, 1, 200);
//        Array.set(intArray, 2, 300);
//        System.out.println("数组元素: " + Arrays.toString(intArray));
//
//        // 7. 注解反射
//        System.out.println("\n=== 7. 注解反射 ===");
//        Class<?> annotatedClass = Class.forName("java.lang.Deprecated");
//        Annotation[] annotations = annotatedClass.getAnnotations();
//        System.out.println("注解类的注解: " + Arrays.toString(annotations));
//
//        // 8. 泛型反射
//        System.out.println("\n=== 8. 泛型反射 ===");
//        Class<?> listClass = Class.forName("java.util.ArrayList");
//        Method addMethod = listClass.getMethod("add", Object.class);
//        Type[] genericParameterTypes = addMethod.getGenericParameterTypes();
//        System.out.println("add方法的泛型参数类型: " + Arrays.toString(genericParameterTypes));
//
//        // 9. 异常处理
//        System.out.println("\n=== 9. 异常处理 ===");
//        try {
//            Constructor<?> invalidConstructor = clazz1.getConstructor(int.class);
//        } catch (NoSuchMethodException e) {
//            System.out.println("捕获异常: " + e.getMessage());
//        }
    }

    /**
     * doWithMethods测试
     */
    @Test
    public void doWithMethodsTest() {
        Object instance = new MyClass();
        Class clazz = instance.getClass();

        //可以对每个方法进行处理，或者过滤后对指定方法处理
        //可以参考这种内部接口用于lambada表达式传参的写法
        ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
            @Override
            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                System.out.println(method.getName());
                try {
                    method.invoke(instance);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
        }, new ReflectionUtils.MethodFilter() {
            @Override
            public boolean matches(Method method) {
                if (method.getName().equalsIgnoreCase("myMethod")) {
                    return true;
                }
                return false;
            }
        });

        //等价于
        ReflectionUtils.doWithMethods(clazz, method -> {
            System.out.println(method.getName());
            try {
                method.invoke(instance);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }, method -> method.getName().equalsIgnoreCase("myMethod"));
    }

    @Test
    public void getTest() {
        Class clazz = MyClass.class;

        Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : allDeclaredMethods) {
            System.out.println(method.getName());
        }
    }
}